package dal.datastore;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;

/**
 * json data file manipulation.
 * 
 * @author Stanley
 * 
 */
public class PhysicalDataManipulation implements IDataStoreConfig {
	/**
	 * insert database to configuration file. Create a JSON file for this
	 * database if create succeed.
	 * 
	 * @param db
	 * @return JSON string for this database.
	 * @throws JsonSyntaxException
	 * @throws JsonIOException
	 * @throws IOException
	 */
	public boolean createDatabase(Database db) throws JsonIOException,
			JsonSyntaxException, IOException {
		Gson gson = new Gson();

		// 1. Get all databases
		Collection<Object> collection;
		try {
			collection = getAllDatabases();
		} catch (Exception e) {
			collection = new ArrayList<Object>();
		}

		// 2. Add new database
		collection.add(db);

		// 3. Write to json file
		String json = gson.toJson(collection);
		FileWriter fw = new FileWriter(new File(dbsPath));
		BufferedWriter bf = new BufferedWriter(fw);
		bf.write(json);
		bf.close();

		// 4. Create a json file to store database's data
		String dbpath = rootpath + "/" + db.getName() + ".json";
		fw = new FileWriter(new File(dbpath));
		bf = new BufferedWriter(fw);
		bf.write("[]");
		bf.close();
		fw.close();

		return true;
	}

	/**
	 * Get all databases from dbsPath
	 * 
	 * @return
	 * @throws JsonIOException
	 * @throws JsonSyntaxException
	 * @throws FileNotFoundException
	 */
	public Collection<Object> getAllDatabases() throws JsonIOException,
			JsonSyntaxException, FileNotFoundException {
		Collection<Object> collection = new ArrayList<>();
		Gson gson = new Gson();
		JsonParser parser = new JsonParser();
		JsonArray array;

		// 1. Get all databases
		array = parser.parse(new FileReader(new File(dbsPath)))
				.getAsJsonArray();
		for (JsonElement element : array) {
			Database database = gson.fromJson(element, Database.class);
			// System.out.println(database.toString());
			collection.add(database);
		}
		return collection;
	}

	/**
	 * Drop a database from config file
	 * 
	 * @param db
	 * @return
	 * @throws IOException
	 */
	/**
	 * @param db
	 * @return
	 * @throws IOException
	 */
	public boolean dropDatabase(Database db) throws IOException {
		Gson gson = new Gson();

		// 1. Get all databases
		Collection<Object> collection;
		try {
			collection = getAllDatabases();
		} catch (Exception e) {
			collection = new ArrayList<Object>();
		}

		// 2. Remove database
		collection.remove(db);

		// 3. Write to json file
		String json = gson.toJson(collection);
		FileWriter fw = new FileWriter(new File(dbsPath));
		BufferedWriter bf = new BufferedWriter(fw);
		bf.write(json);
		bf.close();
		// System.out.println(json);

		// 4. Delete json file that's storing database's data
		String dbpath = rootpath + "/" + db.getName() + ".json";
		File f = new File(dbpath);
		f.delete();

		return true;
	}

	/**
	 * Update database into json file
	 * 
	 * @param db
	 *            database need to updated.
	 * @throws IOException
	 */
	public boolean updateDatabase(Database db) throws IOException {
		Gson gson = new Gson();

		// 1. Get all databases
		Collection<Object> collection;
		try {
			collection = getAllDatabases();
		} catch (Exception e) {
			collection = new ArrayList<Object>();
		}

		// 2. Remove old database and add this again with new.
		Iterator<Object> i = collection.iterator();
		while (i.hasNext()) {
			final Database database = (Database) i.next();
			if (database.equals(db)) {
				database.addTable(db.getTables().get(0));
			}
		}

		// 3. Write to json file
		String json = gson.toJson(collection);
		FileWriter fw = new FileWriter(new File(dbsPath));
		BufferedWriter bf = new BufferedWriter(fw);
		bf.write(json);
		bf.close();

		return true;
	}

	/**
	 * Get all table from database
	 * 
	 * @param dbname
	 *            database name
	 * @return Collection<Object>
	 */
	public Collection<Table> getAllTable(Database db) {
		// 1. Get all databases
		Collection<Object> collection;
		ArrayList<Table> tables = null;
		try {
			collection = getAllDatabases();
		} catch (Exception e) {
			collection = new ArrayList<Object>();
		}

		// 2. Get tables if db is exists
		for (Object object : collection) {
			if (db.equals(object)) {
				tables = ((Database) object).getTables();
				break;
			}
		}
		return tables;
	}

	/**
	 * Get table structure if it exist
	 * 
	 * @param db
	 *            Database
	 * @param tbl
	 *            table
	 * @return
	 */
	public Table getTable(Database db, Table tbl) {
		// 1. Get all databases
		Collection<Object> collection;
		try {
			collection = getAllDatabases();
		} catch (Exception e) {
			collection = new ArrayList<Object>();
		}

		// 2. Get tables if db is exists
		for (Object object : collection) {
			if (db.equals(object)) {
				ArrayList<Table> tables = ((Database) object).getTables();
				for (Table table : tables) {
					if (table.equals(tbl)) {
						return table;
					}
				}
			}
		}
		return null;
	}

	/**
	 * Drop table within database
	 * 
	 * @param db
	 * @param table
	 * @return
	 * @throws IOException
	 */
	public boolean dropTable(Database db, Table table) throws IOException {
		Gson gson = new Gson();

		// 1. Get all databases
		Collection<Object> collection;
		try {
			collection = getAllDatabases();
		} catch (Exception e) {
			collection = new ArrayList<Object>();
		}

		// 2. Get table and remove it if it's exists.
		Iterator<Object> i = collection.iterator();
		while (i.hasNext()) {
			Database database = (Database) i.next();
			if (database.equals(db)) {
				Collection<Table> tables = database.getTables();
				tables.remove(table);
			}
		}
		// 3. Write to json file
		String json = gson.toJson(collection);
		FileWriter fw = new FileWriter(new File(dbsPath));
		BufferedWriter bf = new BufferedWriter(fw);
		bf.write(json);
		bf.close();
		// System.out.println(json);

		// 4. Delete json file that's storing database's data
		String dbpath = rootpath + "/" + db.getName() + ".json";
		File f = new File(dbpath);
		f.delete();

		return true;
	}

	/**
	 * get all data of database
	 * 
	 * @param dbName
	 *            string
	 * @return DatabaseData
	 * @throws FileNotFoundException
	 * @throws JsonSyntaxException
	 * @throws JsonIOException
	 */
	public DatabaseData getDatabaseData(String dbName) throws JsonIOException,
			JsonSyntaxException, FileNotFoundException {
		String datapath = rootpath + "/" + dbName + ".json";
		ArrayList<TableData> tables = new ArrayList<TableData>();
		Gson gson = new Gson();
		JsonParser parser = new JsonParser();
		JsonArray array;

		// 1. Get tables of database
		array = parser.parse(new FileReader(new File(datapath)))
				.getAsJsonArray();

		// 2. Get table structure
		Database dbStruc = new Database(dbName);
		Collection<Table> tableStruc = this.getAllTable(dbStruc);

		// 2. Change to tabledata array
		for (JsonElement element : array) {
			TableData table = gson.fromJson(element, TableData.class);
			for (Table tblStruc : tableStruc) {
				if (tblStruc.getName().equalsIgnoreCase(table.getTableName())) {
					table.setColumns(tblStruc.getColumns());
					break;
				}
			}
			// SETCOLUMNS: {
			// for (Table tblStruc : tableStruc) {
			// if (tblStruc.getName().equalsIgnoreCase(table.getTableName())) {
			// table.setColumns(tblStruc.getColumns());
			// break SETCOLUMNS;
			// }
			//
			// // if (tblStruc.getName().compareToIgnoreCase(
			// // table.getTableName()) == 0) {
			// // table.setColumns(tblStruc.getColumns());
			// // break SETCOLUMNS;
			// // }
			// }
			// }

			tables.add(table);
		}

		// 3. return data
		DatabaseData db = new DatabaseData(dbName, tables);
		return db;

	}

	public boolean updateDatabaseData(DatabaseData db) throws IOException {
		// 1.Convert to json
		Gson gson = new Gson();
		ArrayList<TableData> tables = db.getTableData();
		for (TableData tableData : tables) {
			tableData.setColumns(null);
		}
		String json = gson.toJson(tables);

		String dbPath = rootpath + "/" + db.getDbName() + ".json";
		FileWriter fw;
		try {
			fw = new FileWriter(new File(dbPath));
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		BufferedWriter bf = new BufferedWriter(fw);
		bf.write(json);
		bf.close();
		return true;
		// System.out.println(json);
	}
}
